package application;

import java.util.LinkedList;
import java.util.Vector;

import enums.CellType;
import application.food.Food;

/**
 * represents the Snake Game matrix
 * @author Idan
 *
 */
public class GameMatrix {
	private int _dimension;
	private Vector<Vector<Cell>> _matrix;
	
	/**
	 * constructor. Sets all cells to NONE
	 * @param dimension dimension of game matrix
	 */
	public GameMatrix(int dimension){
		this.set_dimension(dimension);
		this._matrix = new Vector<Vector<Cell>>();
		for(int i = 0; i < dimension; i++){
			this._matrix.add(new Vector<Cell>());
			for(int j = 0; j < dimension; j++)
				this._matrix.lastElement().add(new Cell(new Coordinate(i,j)));
		}
	}
	/**
	 * sets the cell type at specified coordinates
	 * @param x x coordinate
	 * @param y y coordinate
	 * @param type application.enums.CellType type of cell
	 * @param food the type of food this cell contains. Null if none
	 */
	public void setCell(int x, int y, CellType type, Food food){
		this._matrix.get(x).get(y).set_type(type);
		this._matrix.get(x).get(y).set_food(food);
	}
	/**
	 * return the type of element at coordinate (x,y)
	 * @param x x coordinate
	 * @param y y coordinate
	 * @return application.enums.CellType type of cell
	 * @throws IllegalArgumentException if either argument is out of bounds
	 */
	public Cell getCell(int x, int y) throws IllegalArgumentException{
		if(x < this.get_dimension())
			if(y < this.get_dimension())
				return this._matrix.get(x).get(y);
			else
				throw new IllegalArgumentException("y coordinate out of bounds");
		else throw new IllegalArgumentException("x coordinate out of bounds");		
	}
	/**
	 * setter
	 * @param _dimension
	 */
	public void set_dimension(int _dimension) {
		this._dimension = _dimension;
	}
	/**
	 * setter
	 * @return
	 */
	public int get_dimension() {
		return _dimension;
	}
	/**
	 * initializes snake body according to its size and the board dimensions and
	 * 	returns it to the snake object
	 * @param size snake initial size
	 * @throws IllegalArgumentException if size is larger than dimension
	 * @returns a linked list representing the snake body
	 */
	public LinkedList<Coordinate> InitSnakeBody(int size) throws IllegalArgumentException{
		int middle = this.get_dimension() / 2;
		int y,x;
		LinkedList<Coordinate> ans = new LinkedList<Coordinate>();
		if(size < this.get_dimension()){
			if(size <= middle){
				x = middle;
				y = middle;
			}
			else{
				x = size;
				y = 0;
			}
			this.setCell(x, y, CellType.SNAKEHEAD,null);
			ans.add(new Coordinate(x,y));
			int j = x - 1;
			for(int i = 0; i < size - 1 ; i++){
				this.setCell(j, y, CellType.SNAKEBODY,null);
				ans.add(new Coordinate(j,y));
				j--;
			}
			return ans;
		}else throw new IllegalArgumentException("snake is bigger than board");
	}
	/**
	 * searches the game matrix for the snake's head
	 * @return Coordinate of the snake's head
	 */
	public Coordinate getSnakeHead(){
		for(int i = 0; i < this.get_dimension(); i++)
			for(int j = 0; j < this.get_dimension(); j++){
				Cell c = this.getCell(i, j);
				if(c != null && c.get_type() == CellType.SNAKEHEAD)
					return c.get_coord();
			}
		return null;
	}
	
	/**
	 * searches the game matrix for the snake's tail
	 * @return Coordinate of the snake's tail
	 */
	public Coordinate getSnakeTail(){
		for(int i = 0; i < this.get_dimension(); i++)
			for(int j = 0; j < this.get_dimension(); j++){
				Cell c = this.getCell(i, j);
				if(c != null && c.get_type() == CellType.SNAKETAIL)
					return c.get_coord();
			}
		return null;
	}
	/**
	 * advances the snake's tail by one cell
	 * @param oldTail old tail coordinate
	 */
	public void advanceTail(Coordinate oldTail) {
		int xOld = oldTail.get_x();
		int yOld = oldTail.get_y();
		GameManager.print("changing ("+xOld+","+yOld+") to NONE");
		this.setCell(xOld, yOld, CellType.NONE,null);
	}
	/**
	 * advances the snake's head by one cell
	 * @param oldHead old head coordinates
	 * @param newHead new head coordinates
	 */
	public void advanceHead(Coordinate oldHead, Coordinate newHead) {
		int xOld = oldHead.get_x();
		int yOld = oldHead.get_y();
		int xNew = newHead.get_x();
		int yNew = newHead.get_y();
		GameManager.print("changing ("+xOld+","+yOld+") to SNAKEBODY");
		this.setCell(xOld, yOld, CellType.SNAKEBODY,null);
		GameManager.print("changing ("+xNew+","+yNew+") to SNAKEHEAD");
		this.setCell(xNew, yNew, CellType.SNAKEHEAD,null);
	}
	/**
	 * prints the matrix to console
	 */
	public void print() {
		if(GameManager.DEBUG){
			System.out.println("==================================================");
			for(int i = 0; i < this.get_dimension(); i++){
				for(int j = 0; j < this.get_dimension(); j++){
					System.out.print(CellType.toString(this.getCell(j, i).get_type())
							+" ");
				}
				System.out.println();
			}		
		}
	}
	public Cell getCell(Coordinate c) {
		return this.getCell(c.get_x(), c.get_y());
	}
	public void setCell(Coordinate coor, CellType type, Food food) {
		this.setCell(coor.get_x(), coor.get_y(), type, food);
	}
}
